iT邦幫忙

2026 iThome 鐵人賽

DAY 6
0
JavaScript

重新拿回思考力!一起來用 JavaScript 打造 CLI 好玩遊戲區!系列 第 6

Day6 推箱子5:不知道想法好不好,就打造原型!

  • 分享至 

  • xImage
  •  

昨天結束時我提到了一個 Good Idea:設計一個按鈕,人踩進去之後可以彈出箱子,不過我其實也不知道它真正玩起來到底會不會是一個 Good Idea,必須要打造出來玩看看才會知道。

原型的概念是我最近閱讀《做自己的生命設計師》這本書時獲得的概念,意思是在產品設計的過程中,藉由打造出多個具備差異的產品,藉此來比較哪一個做法更好。我很喜歡這個概念,而且我想也很適合放在這裡:我不確定推箱子是不是一個好的想法,但我可以藉由打造出一個原型來實測看看,也很符合這個系列的精神,我們就是不斷地小步測試,直到找到更好的做法。

一樣我們就先來想想要實踐這個原型我們需要哪些東西:

  1. 要有一個變數來儲存按鈕座標
  2. 按鈕是哪一個符號?
  3. 當人踩進去的時候可以把箱子踢出一格

這時候就想到一個新的問題,箱子是不論在哪一個位置都可以被移動一格嗎?還是必須在一個特定的位置,往特定的方向移動一格呢?

回顧一下我們的目的是要解決靠在邊上的箱子沒有辦法離開邊邊的問題,所以我會想先嘗試限制箱子必須要在邊邊才可以被推出。

那我們就用自然語言來寫寫看實作的步驟吧:

  1. 制定變數 button
  2. 符號就用 x 好了
  3. 如果 player 座標等於 button 座標,而且 box 在指定的位置(先假設是左上角的 1,1 這個位置),就向 x 軸移動一格

那麼我們就來寫寫看吧

首先加一個變數

let button = pickRandomEmptyCell(map,[player,box,goal]) // 加上按鈕的位置

接著把它印出來,在 render 函式中補上

if (player.x === x && player.y === y) {
                row += `\x1b33m@\x1b[0m`
            } else if (box.x === x && box.y === y) {
                row += `\x1b[34m$\x1b[0m`
            } else if (goal.x === x && goal.y === y) {
                row += `\x1b[31m.\x1b[0m`
             // 補上下面這一段,印出 button
            } else if (button.x === x && button.y === y) {
                row += `\x1b[31mx\x1b[0m`
            } else if (cell === '@' || cell === '$' || cell === '.') {
                row += ' '
            } else {
                row += `\x1b[32m${cell}\x1b[0m`
            }

最後是我們的遊戲邏輯,要在 player 踩上去 button 的時候彈出 box

試著在監聽的區塊寫下以下的邏輯

// 接收到這些狀況的時候被觸發,'data' 指有新資料進來的時候
process.stdin.on('data', (key) => {

    // ... 監聽上下左右移動的程式碼不變

    render();

    if(player.y === button.y && player.x === button.x && box.x === 1 && box.y === 1){
        box.x += 1;
        render(); // 要記得重新 render 才會在畫面上更新唷
    }
    
    
    // ... 下方獲勝判定程式碼不變

});

結果因為我們先前撰寫的只要箱子靠到死角就會結束遊戲的邏輯導致一靠到角落就結束遊戲,於是我們來到另外一個抉擇點:要改變箱子的指定位置,還是要取消靠到最左上就立即失敗的邏輯

由於我們現在主要是要測試彈出的邏輯可不可行,所以我想先選擇改變指定的位置。

讓我們修正剛剛的邏輯,把原本指定 x 和 y 都等於 1 ,改為 x 為 2 , y 為 1

if(player.y === button.y && player.x === button.x && box.x === 2 && box.y === 1){
        box.x += 1;
        render();
    }

實際測試之後確定可以向右邊推出一格

6-1 踩箱子往右邊推一格

但很顯然的我們不會因此而滿足!! 就像前面所說的,這個設計的初衷應該是要挽救無可挽救的結局,所以要在非死角的任意邊上都可以彈出。來寫寫看這個邏輯,一樣用自然語言的方式先試著描述看看可被彈出的條件:

box 的任一邊為牆壁,且不在死角的位置,則朝向牆壁的反方向移動一格

由於之前已經有寫好的 top、bottom、left、right 來判斷是否位於邊上,也有 isDeadlock 來判斷是否位於死角,我們剛好可以拿來用,所以將這一段判斷移動到這些變數的後方

    const { top,bottom,left,right,isDeadlock } = checkBox (map,box);

    // 這邊來寫推出箱子的邏輯,首先先判斷如果是死角就不進這個流程
    if(player.y === button.y && player.x === button.x && !isDeadlock){
        if (top){
            box.y += 1;
        }else if (bottom){
            box.y -= 1;
        }else if (left){
            box.x += 1;
        }else if (right){
            box.x -= 1;
        }
        render();
    }

    if (isDeadlock) {
        process.stdout.write("\n箱子卡住了,遊戲結束\n");
        process.exit();
    }

現在就可以拯救那些不小心靠到邊邊的箱子了!!

再多做點小變化

原型打造好了,不過這樣玩感覺有點普通,這就是我們的使用者體驗獲得升級的時刻了!從使用者的角度給自己一點回饋:如果加上一些條件會不會更好?比方說要將另一個箱子推進去,就可以將箱子推出來?

那我們就需要第二個箱子?需要第二個目標點嗎?
還是不管哪一個箱子,只要有一個進到目標點就可以?

如果不確定哪一種更好的話,最好的方式就是藉由打造原型來實測看看!! 明天一起來嘗試看看吧!

如果你想要看完整的程式碼,可以參考這裡:Day6 程式碼


上一篇
Day5 推箱子4:跟隨使用者的體驗來打造隨機地圖!
下一篇
Day7 推箱子6 :先窮舉後精簡的練習
系列文
重新拿回思考力!一起來用 JavaScript 打造 CLI 好玩遊戲區!7
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言